/*
 * \brief  PCI bus access
 * \author Christian Helmuth
 * \date   2008-10-22
 *
 * Design notes:
 *
 * - Provide a virtual PCI bus in pci_bus.cc
 *   - Grab all accessible devices at pci_drv and populate virtual bus. (This
 *     also works if parents/device managers limit device access in the
 *     future.)
 *   - Provide config space access and _temporarily_ limit write access to
 *     safe device-specific registers. (In the future, the PCI driver _must_
 *     enforce capability-based access permissions.)
 * - Provide DDE kit C interface in pci.cc
 *   - Read/write config space
 *   - Convenience functions ala enable_device/get_irq (optional)
 */

/*
 * Copyright (C) 2008-2009 Christian Helmuth
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#include <base/printf.h>

extern "C" {
#include <dde_kit/pci.h>
}

#include "pci_bus.h"


struct dde_kit_pci_dev : public Dde_kit::Pci_device { };


static Dde_kit::Pci_bus *pci_bus()
{
	static Dde_kit::Pci_bus _pci_bus;

	return &_pci_bus;
}


/********************************
 ** Configuration space access **
 ********************************/

extern "C" int dde_kit_pci_readb(int slot, int func, int pos, dde_kit_uint8_t *val)
{
	unsigned char devfn = Dde_kit::Pci_bus::devfn(slot, func);

	try {
		*val = pci_bus()->config_read(devfn, pos, Pci::Device::ACCESS_8BIT) & 0xff;
	} catch (...) {
		return -1;
	}

	return 0;
}


extern "C" int dde_kit_pci_readw(int slot, int func, int pos, dde_kit_uint16_t *val)
{
	unsigned char devfn = Dde_kit::Pci_bus::devfn(slot, func);

	try {
		*val = pci_bus()->config_read(devfn, pos, Pci::Device::ACCESS_16BIT) & 0xffff;
	} catch (...) {
		return -1;
	}

	return 0;
}


extern "C" int dde_kit_pci_readl(int slot, int func, int pos, dde_kit_uint32_t *val)
{
	unsigned char devfn = Dde_kit::Pci_bus::devfn(slot, func);

	try {
		*val = pci_bus()->config_read(devfn, pos, Pci::Device::ACCESS_32BIT);
	} catch (...) {
		return -1;
	}

	return 0;
}


extern "C" int dde_kit_pci_writeb(int slot, int func, int pos, dde_kit_uint8_t val)
{
	unsigned char devfn = Dde_kit::Pci_bus::devfn(slot, func);

	try {
		pci_bus()->config_write(devfn, pos, val, Pci::Device::ACCESS_8BIT);
	} catch (...) {
		return -1;
	}

	return 0;
}

extern "C" int dde_kit_pci_writew(int slot, int func, int pos, dde_kit_uint16_t val)
{
	unsigned char devfn = Dde_kit::Pci_bus::devfn(slot, func);

	try {
		pci_bus()->config_write(devfn, pos, val, Pci::Device::ACCESS_16BIT);
	} catch (...) {
		return -1;
	}

	return 0;
}


extern "C" int dde_kit_pci_writel(int slot, int func, int pos, dde_kit_uint32_t val)
{
	unsigned char devfn = Dde_kit::Pci_bus::devfn(slot, func);

	try {
		pci_bus()->config_write(devfn, pos, val, Pci::Device::ACCESS_32BIT);
	} catch (...) {
		return -1;
	}

	return 0;
}


/***************************
 ** Convenience functions **
 ***************************/

extern "C" struct dde_kit_pci_dev *dde_kit_pci_find_devfn(int slot, int func)
{
	unsigned char devfn = Dde_kit::Pci_bus::devfn(slot, func);

	try {
		return (dde_kit_pci_dev *)pci_bus()->lookup(devfn);
	} catch (...) {
		return 0;
	}
}


extern "C" struct dde_kit_pci_dev *dde_kit_pci_next_device(struct dde_kit_pci_dev *start)
{
	try {
		return (dde_kit_pci_dev *)pci_bus()->next_device(start);
	} catch (...) {
		return 0;
	}
}


extern "C" unsigned char dde_kit_pci_get_devfn(struct dde_kit_pci_dev *dev)
{
	try {
		return dev->devfn();
	} catch (...) {
		PERR("unexpected error");
		return 0;
	}
}


#if 0
extern "C" int dde_kit_pci_enable_device(struct dde_kit_pci_dev *dev)
{
	/* switch power on */
	/* initialize I/O resources */
}


extern "C" int dde_kit_pci_disable_device(struct dde_kit_pci_dev *dev)
{
}


extern "C" int dde_kit_pci_set_master(struct dde_kit_pci_dev *dev)
{
}
#endif


/********************
 ** Initialization **
 ********************/

extern "C" void dde_kit_pci_init(void)
{
	Dde_kit::Pci_bus *bus = pci_bus();

	/* XXX remove after debugging */
	bus->show_all();
}
